<!DOCTYPE html>
<html lang="en">

  <head>

<!-- AdThrive Head Tag Manual -->
<script data-no-optimize="1" data-cfasync="false">
(function(w, d) {
	w.adthrive = w.adthrive || {};
	w.adthrive.cmd = w.
	adthrive.cmd || [];
	w.adthrive.plugin = 'adthrive-ads-manual';
	w.adthrive.host = 'ads.adthrive.com';var s = d.createElement('script');
	s.async = true;
	s.referrerpolicy='no-referrer-when-downgrade';
	s.src = 'https://' + w.adthrive.host + '/sites/643436a4e6d20859e40a446b/ads.min.js?referrer=' + w.encodeURIComponent(w.location.href) + '&cb=' + (Math.floor(Math.random() * 100) + 1);
	var n = d.getElementsByTagName('script')[0];
	n.parentNode.insertBefore(s, n);
})(window, document);
</script>
<!-- End of AdThrive Head Tag -->





    <!-- Global site tag (gtag.js) - Google Analytics -->



  <script async src="https://www.googletagmanager.com/gtag/js?id=UA-79254642-6"></script>

    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());

      gtag('config', 'UA-79254642-6');
    </script>

    <meta charset="utf-8">
    <title>Building tooltips with d3.js</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="How to add tooltips on a d3.js plot: setting up, customizing, positioning and more. Explanation and reproducible code.">
    <meta name="keywords" content="Data,Dataviz,Datavisualization,Javascript, JS, d3.js, axis, x, y">
    <meta name="author" content="Yan Holtz">
  <link rel="icon" href="../img/logo/D3_single_small.png">

    <meta property="og:title" content="Building tooltips with d3.js">
    <meta property="og:image" content="img/overview_RGG.png">
    <meta property="og:description" content="How to add tooltips on a d3.js plot: setting up, customizing, positioning and more. Explanation and reproducible code.">

    <!-- Bootstrap core CSS -->
    <link href="../vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom fonts for this template -->
    <link href="../vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">

    <!-- Custom styles for this template -->
    <link href="../css/agency.css" rel="stylesheet">

    <!-- PRISM -->
    <script src="../LIB/prism.js"></script>
    <link href="../LIB/prism.css" rel="stylesheet" />

    <!-- D3.JS v4 -->
    <script src="../js/d3.v4.js"></script>
    <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>

    <!-- JQUERY -->
    <script src="../vendor/jquery/jquery.min.js"></script>

    <!-- HTML TO CANVAS -->
    <script src="../js/html2canvas.js"></script>

    <!-- Function to parse html and js code chunks made by Yan Holtz -->
    <script src="../js/myParser.js"></script>

    <!-- Bootstrap js -->
    <script src="../vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
    <script src="../js/agency.min.js"></script>

  </head>



  <body data-spy="scroll" data-target="#myScrollspy" data-offset="1">


    <!-- THIS ALLOWS TO INSERT THE MENU THAT IS STORED IN A MENU.HTML FILE-->
    <nav class="navbar navbar-expand-lg fixed-top" id="mainNav"></nav>
    <script>
    $(function(){
      $("#mainNav").load("../html_chunk/menu.html");
    });
    </script>

    <!-- THIS ALLOWS TO INSERT THE MODAL OF THE MENU THAT IS STORED IN A MENU_MODAL.HTML FILE-->
    <div id="modal_menu_insertion"> </div>
    <script>
    $(function(){
      $("#modal_menu_insertion").load("../html_chunk/menu_modal.html");
    });
    </script>



    <!-- Header -->
    <header class="masthead" style="padding-top: 150px; padding-bottom: 80px">
      <div class="textlanding">
          <h1>Building tooltips with d3.js</h1>
          <hr class="short_hr">
          <br>
          <ul class="list-inline social-buttons">
            <li class="list-inline-item">
              <a href="https://twitter.com/R_Graph_Gallery">
                <i class="fa fa-twitter"></i>
              </a>
            </li>
            <li class="list-inline-item social-buttons">
              <a href="https://github.com/holtzy">
                <i class="fa fa-github" style="color: white"></i>
              </a>
            </li>
            <li class="list-inline-item social-buttons">
              <a href="https://www.linkedin.com/in/yan-holtz-2477534a/">
                <i class="fa fa-linkedin"></i>
              </a>
            </li>
          </ul>
          <br><br>
          <p style="max-width: 700px; margin: auto">This is document gives a few insights on how to add <u>tooltips</u> with d3.js. It is composed by several interactive examples, allowing to play with the code to understand better how it works.</p>
      </div>
    </header>








    <!-- TABLE of CONTENT -->
    <div>
        <nav class="col-sm-3 col-4" id="myScrollspy">
          <ul class="nav nav-pills flex-column">
            <li class="nav-item">
              <a class="nav-link active" href="#mostbasic">Most basic tooltip</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#customContent">Custom content with HTML</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#position">Tooltip position</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#template">Template</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#example">More examples</a>
            </li>
          </ul>
        </nav>
    </div>















<!-- ==================== MOST BASIC TOOLTIP ==================== -->

<section id="mostbasic" class="bg">
<div class="container" >
  <h1>Most basic tooltip</h1>
  <hr>
  <div class="row">

<!-- ==================== GRAPH SECTION = WHERE THE GRAPH APPEARS ==================== -->
  <div class="col-lg-4 .align-middle">
    <div style="position: -webkit-sticky; position: sticky; top: 120px">
      <div class="bg-light" id="res-basicTrigger" ></div>
      <br>
      <div>
        <h5>Steps:</h5>
        <ul>
          <br>
          <li>First, a basic circle is added using <code>.svg</code>. Note that it would work the same with any type of <code>svg</code> element.</li>
          <br>
          <li>Then a new <code>div</code> is added, with a bit of text in it and a few features. It is actually the tooltip! But it is hidden is css with <code>visibility: hidden</code>.</li>
          <br>
          <li>Lastly, 3 different javascript functions are called when an action is done on the circle: hovering it, moving, and leaving it. This will reveal or hide the tooltip.</li>
        </ul>
      </div>
    </div>
  </div>



<!-- ==================== CODE SECTION = WHERE THE CODE APPEARS ==================== -->
<div class="col-lg-8 text-center .align-middle">

<!-- ========= show html code ============== -->
<aside style="position: -webkit-sticky; position: sticky; top: 120px">&larr; Edit me! </aside>
<pre class="language-html"><code id="html-basicTrigger" contenteditable="true"><xmp><!DOCTYPE html>
<meta charset="utf-8">

<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>

</xmp></code></pre>
<!-- ==================================== -->



<!-- ========= show JS code ==============  -->
<pre class="language-js"><code id="js-basicTrigger" contenteditable="true"><xmp><script>

// Start by creating the svg area
var svg = d3.select("#my_dataviz")
  .append("svg")
    .attr("width", 400)
    .attr("height", 400)

// Append a circle
svg.append("circle")
  .attr("id", "circleBasicTooltip")
  .attr("cx", 150)
  .attr("cy", 200)
  .attr("r", 40)
  .attr("fill", "#69b3a2")

// Create a tooltip
var tooltip = d3.select("#my_dataviz")
  .append("div")
    .style("position", "absolute")
    .style("visibility", "hidden")
    .text("I'm a circle!");

// Show, move and hide the tooltip
d3.select("#circleBasicTooltip")
  .on("mouseover", () => tooltip.style("visibility", "visible"))
  .on("mousemove", () => tooltip.style("top", (event.offsetY-30)+"px").style("left",(event.offsetX+10)+"px"))
  .on("mouseout", () => tooltip.style("visibility", "hidden"));

</script></xmp></code></pre>
<!-- ==================================== -->







<!-- ==================== JAVASCRIPT SECTION : EXECUTE THIS CODE AND MAKE IT INTERACTIVE ==================== -->
<script>
// At the beginning, I read the html and JS fragments
myHtmlParser('html-basicTrigger', 'res-basicTrigger')
myJSParser('js-basicTrigger', 'res-basicTrigger')

// If the user change the JS fragment, I run it again:
document.getElementById('js-basicTrigger').addEventListener("input", function() {
d3.select('#res-basicTrigger').html("");
myHtmlParser('html-basicTrigger', 'res-basicTrigger');
myJSParser('js-basicTrigger', 'res-basicTrigger');
})

// If the user change the HTML fragment, I run it again:
document.getElementById('html-basicTrigger').addEventListener("input", function() {
d3.select('#res-basicTrigger').html("");
myHtmlParser('html-basicTrigger', 'res-basicTrigger');
myJSParser('js-basicTrigger', 'res-basicTrigger');
})
</script>

</div>


  </div>
</div>
</section>
<br>


















<!-- ==================== CUSTOM TOOLTIP WITH HTML ==================== -->

<section id="customContent" class="bg">
<div class="container" >
  <h1>Custom tooltip content with <code>html</code></h1>
  <hr>
  <div class="row">

<!-- ==================== GRAPH SECTION = WHERE THE GRAPH APPEARS ==================== -->
  <div class="col-lg-4 .align-middle">
    <div style="position: -webkit-sticky; position: sticky; top: 120px">
      <div class="bg-light" id="res-customContent" ></div>
      <br>
      <div>
        <h5>Steps:</h5>
        <ul>
          <br>
          <li>Good news! you can put whatever <code>html</code> element in your tooltip. Thus, absolutely any customization is doable.</li>
        </ul>
      </div>
    </div>
  </div>



<!-- ==================== CODE SECTION = WHERE THE CODE APPEARS ==================== -->
<div class="col-lg-8 text-center .align-middle">

<!-- ========= show html code ============== -->
<aside style="position: -webkit-sticky; position: sticky; top: 120px">&larr; Edit me! </aside>
<pre class="language-html"><code id="html-customContent" contenteditable="true"><xmp><!DOCTYPE html>
<meta charset="utf-8">

<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="div_customContent"></div>

</xmp></code></pre>
<!-- ==================================== -->



<!-- ========= show JS code ==============  -->
<pre class="language-js"><code id="js-customContent" contenteditable="true"><xmp><script>

// Start by creating the svg area
var svg_customContent = d3.select("#div_customContent")
  .append("svg")
    .attr("width", 400)
    .attr("height", 400)

// Append a circle
svg_customContent.append("circle")
  .attr("id", "circleCustomTooltip")
  .attr("cx", 150)
  .attr("cy", 200)
  .attr("r", 40)
  .attr("fill", "#69b3a2")

// Create a tooltip
var tooltip2 = d3.select("#div_customContent")
  .append("div")
    .style("position", "absolute")
    .style("visibility", "hidden")
    .style("background-color", "white")
    .style("border", "solid")
    .style("border-width", "1px")
    .style("border-radius", "5px")
    .style("padding", "10px")
    .html(`
      <p>I'm a tooltip written in HTML</p>
      <img src='https://github.com/holtzy/D3-graph-gallery/blob/master/img/section/ArcSmal.png?raw=true'></img>
      <br>Fancy<br>
      <span style='font-size: 40px;'>Isn't it?</span>
    `);

// Show, move and hide the tooltip
d3.select("#circleCustomTooltip")
  .on("mouseover", () => tooltip2.style("visibility", "visible"))
  .on("mousemove", () => tooltip2.style("top", (event.offsetY+30)+"px").style("left",(event.offsetX-90)+"px"))
  .on("mouseout", () => tooltip2.style("visibility", "hidden"));

</script></xmp></code></pre>
<!-- ==================================== -->







<!-- ==================== JAVASCRIPT SECTION : EXECUTE THIS CODE AND MAKE IT INTERACTIVE ==================== -->
<script>
// At the beginning, I read the html and JS fragments
myHtmlParser('html-customContent', 'res-customContent')
myJSParser('js-customContent', 'res-customContent')

// If the user change the JS fragment, I run it again:
document.getElementById('js-customContent').addEventListener("input", function() {
d3.select('#res-customContent').html("");
myHtmlParser('html-customContent', 'res-customContent');
myJSParser('js-customContent', 'res-customContent');
})

// If the user change the HTML fragment, I run it again:
document.getElementById('html-customContent').addEventListener("input", function() {
d3.select('#res-customContent').html("");
myHtmlParser('html-customContent', 'res-customContent');
myJSParser('js-customContent', 'res-customContent');
})
</script>

</div>


  </div>
</div>
</section>
<br>
















<!-- ==================== TOOLTIP POSITION ==================== -->

<section id="position" class="bg">
<div class="container" >
  <h1>Control tooltip position: <code>event.offsetX</code> or <code>d3.select(this)</code></h1>
  <hr>
  <p>There are a number of different ways to recover the mouse position and use it to control the tooltip position.
  <br><br>
  <ul>
    <li>
      <p><code>event.offsetX</code> and <code>event.offsetY</code> are properties of <a href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent">MouseEvent</a>. They provide the mouse position when the event happens. Used in the above example. The code looks like this:</p>
      <pre class="language-js"><code id="js-customContent" contenteditable="false"><xmp>
      tooltip
        .style("top", (event.offsetY)+"px")
        .style("left",(event.offsetX)+"px")
      </xmp></code></pre>
      <p>You can add a numeric value next to <code>event.offsetX</code> or Y to adjust the tooltip position</p>
      <p><u>Note</u>: As well as <code>event.offsetX</code>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent">MouseEvent</a> provides a number of other position properties like <code>pageX</code>, <code>screenX</code> and <code>clientX</code>. See <a href="https://stackoverflow.com/q/6073505/13762264">here</a> for more information about the differences between these.</p>
    </li>
    <br><br>
    <li>
      <code>d3.select(this)</code> is a second option. It selects the element that is hovered. Thus, it is possible to get whatever attribute or style of this element, like its position. If the element is a circle, you can get the <code>cx</code> attribute, which is the horizontal positioning.</p>
      <pre class="language-js"><code id="js-customContent" contenteditable="false"><xmp>
      tooltip
        .style("top", d3.select(this).attr("cy") + "px");
        .style("left", d3.select(this).attr("cx") + "px")
      </xmp></code></pre>
    </li>
    <br><br>
    <li>
      <code>d3.mouse(this)</code> another option. Not sure of the differences with 2 others above.</p>
      <pre class="language-js"><code id="js-customContent" contenteditable="false"><xmp>
      tooltip
        .style("top",  (d3.mouse(this)[1]) + "px")
        .style("left", (d3.mouse(this)[0]) + "px")
      </xmp></code></pre>
    </li>
  </ul>
  <br><br>
</div>
</section>

















<!-- ==================== TEMPLATE ==================== -->

<section id="template" class="bg">
<div class="container" >
  <h1>Template for tooltips</h1>
  <hr>
  <div class="row">

<!-- ==================== GRAPH SECTION = WHERE THE GRAPH APPEARS ==================== -->
  <div class="col-lg-5 .align-middle">
    <div style="position: -webkit-sticky; position: sticky; top: 120px">
      <div class="bg-light" id="res-template" ></div>
      <br>
      <div>
        <h5>Notes:</h5>
        <ul>
          <br>
          <li>Learn more about heatmap <a href="../heatmap.html">here</a>.</li>
          <br>
          <li>It is a good practice to define the mouseover, mousemove and mouseleave behaviour in 3 distinct functions.</li>
          <br>
          <li>Here the square style is slightly modified when the element is hovered. It allows to make more obvious which element is observed.</li>
        </ul>
      </div>
    </div>
  </div>



<!-- ==================== CODE SECTION = WHERE THE CODE APPEARS ==================== -->
<div class="col-lg-7 text-center .align-middle">

<!-- ========= show html code ============== -->
<aside style="position: -webkit-sticky; position: sticky; top: 120px">&larr; Edit me! </aside>
<pre class="language-html"><code id="html-template" contenteditable="true"><xmp><!DOCTYPE html>
<meta charset="utf-8">

<!-- Load d3.js & plugin-->
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>

<!-- Create a div where the graph will take place -->
<div id="div_template"></div>

</xmp></code></pre>
<!-- ==================================== -->



<!-- ========= show JS code ==============  -->
<pre class="language-js"><code id="js-template" contenteditable="true"><xmp><script>

// set the dimensions and margins of the graph
var margin = {top: 20, right: 25, bottom: 30, left: 40},
  width = 450 - margin.left - margin.right,
  height = 450 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#div_template")
.append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
.append("g")
  .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");

//Read the data
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/heatmap_data.csv", function(data) {

  // Labels of row and columns -> unique identifier of the column called 'group' and 'variable'
  var myGroups = d3.map(data, function(d){return d.group;}).keys()
  var myVars = d3.map(data, function(d){return d.variable;}).keys()

  // Build X scales and axis:
  var x = d3.scaleBand()
    .range([ 0, width ])
    .domain(myGroups)
    .padding(0.05);
  svg.append("g")
    .style("font-size", 15)
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x).tickSize(0))
    .select(".domain").remove()

  // Build Y scales and axis:
  var y = d3.scaleBand()
    .range([ height, 0 ])
    .domain(myVars)
    .padding(0.05);
  svg.append("g")
    .style("font-size", 15)
    .call(d3.axisLeft(y).tickSize(0))
    .select(".domain").remove()

  // Build color scale
  var myColor = d3.scaleSequential()
    .interpolator(d3.interpolateInferno)
    .domain([1,100])

  // create a tooltip
  var Tooltip = d3.select("#div_template")
    .append("div")
    .style("opacity", 0)
    .attr("class", "tooltip")
    .style("background-color", "white")
    .style("border", "solid")
    .style("border-width", "2px")
    .style("border-radius", "5px")
    .style("padding", "5px")

  // Three function that change the tooltip when user hover / move / leave a cell
  var mouseover = function(d) {
    Tooltip
      .style("opacity", 1)
    d3.select(this)
      .style("stroke", "black")
      .style("opacity", 1)
  }
  var mousemove = function(d) {
    Tooltip
      .html("The exact value of<br>this cell is: " + d.value)
      .style("left", (d3.mouse(this)[0]+70) + "px")
      .style("top", (d3.mouse(this)[1]) + "px")
  }
  var mouseleave = function(d) {
    Tooltip
      .style("opacity", 0)
    d3.select(this)
      .style("stroke", "none")
      .style("opacity", 0.8)
  }


  // add the squares
  svg.selectAll()
    .data(data, function(d) {return d.group+':'+d.variable;})
    .enter()
    .append("rect")
      .attr("x", function(d) { return x(d.group) })
      .attr("y", function(d) { return y(d.variable) })
      .attr("rx", 4)
      .attr("ry", 4)
      .attr("width", x.bandwidth() )
      .attr("height", y.bandwidth() )
      .style("fill", function(d) { return myColor(d.value)} )
      .style("stroke-width", 4)
      .style("stroke", "none")
      .style("opacity", 0.8)
    .on("mouseover", mouseover)
    .on("mousemove", mousemove)
    .on("mouseleave", mouseleave)
})

</script></xmp></code></pre>
<!-- ==================================== -->







<!-- ==================== JAVASCRIPT SECTION : EXECUTE THIS CODE AND MAKE IT INTERACTIVE ==================== -->
<script>
// At the beginning, I read the html and JS fragments
myHtmlParser('html-template', 'res-template')
myJSParser('js-template', 'res-template')

// If the user change the JS fragment, I run it again:
document.getElementById('js-template').addEventListener("input", function() {
d3.select('#res-template').html("");
myHtmlParser('html-template', 'res-template');
myJSParser('js-template', 'res-template');
})

// If the user change the HTML fragment, I run it again:
document.getElementById('html-template').addEventListener("input", function() {
d3.select('#res-template').html("");
myHtmlParser('html-template', 'res-template');
myJSParser('js-template', 'res-template');
})
</script>

</div>


  </div>
</div>
</section>
<br>











<!-- ==================== MORE EXAMPLES ==================== -->

<section class="bg" id="example">
  <div class="container">

    <h1>Examples</h1>
    <hr>
    <p>A set of d3.js chart examples with reproducible code that use the tooltip concepts described above:</p>
    <div id="portfolio-items" class="row">

      <div class="col-md-4 col-sm-6 portfolio-item">
        <a class="portfolio-link" href="boxplot_horizontal.html">
          <img class="img-fluid" src="../img/graph/boxplot_template.png" alt="">
        </a>
      </div>
      <div class="col-md-4 col-sm-6 portfolio-item">
        <a class="portfolio-link" href="scatter_tooltip.html">
          <img class="img-fluid" src="../img/graph/scatter_tooltip.png" alt="">
        </a>
      </div>
      <div class="col-md-4 col-sm-6 portfolio-item">
        <a class="portfolio-link" href="graph/interactivity_tooltip.html#template">
          <img class="img-fluid" src="../img/graph/heatmap_tooltip.gif" alt="">
        </a>
      </div>

    </div>

  </div>
</section>















<!-- ============================ RELATED BLOCKS ============================ -->

<section id="related" class="bg-light" style="padding-top: 70px; padding-bottom: 70px">
  <div class="container">
    <div class="row">
      <div class="col-lg-5 text-center .align-middle" style="border-right: solid; ">
        <div style="display: table; height: 150px; overflow: hidden;">
          <div style="display: table-cell; vertical-align: middle;">
            <h2 class="section-heading text-uppercase" style="color: black">Related blocks</h2>
          </div>
        </div>
      </div>
      <div class="col-lg-5">
        <div style="display: table; height: 150px; overflow: hidden;">
          <div style="display: table-cell; vertical-align: middle;">
            <ul>
            <li><i>Simple D3 tooltip</i> by <a href="http://bl.ocks.org/biovisualize/1016860">Chris Viau</a></li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>















<!-- ============================ CONTACT SECTION ============================ -->

  <!-- ANCHOR -->
  <a name="contactanchor"></a>


<section id="contact" class="bg" style="background-color: white"></section>

<!-- THIS ALLOWS TO INSERT THE CONTACT CHUNK THAT IS STORED IN A CONTACT.HTML FILE-->
<script>
$(function(){
  $("#contact").load("../html_chunk/contact.html");
});
</script>













<!-- ============================ FOOTER SECTION ============================ --><footer class="bg-light" id="myFooter"></footer>

<!-- THIS ALLOWS TO INSERT THE FOOTER THAT IS STORED IN A FOOTER.HTML FILE-->
<script>
$(function(){
  $("#myFooter").load("../html_chunk/footer.html");
});
</script>

<!-- ============================ -->


























  </body>

</html>
